package org.fhawk.tinyspring.ioc;

import org.fhawk.tinyspring.core.ClassHelper;
import org.fhawk.tinyspring.core.error.InitializationError;
import org.fhawk.tinyspring.ioc.annotation.Impl;
import org.fhawk.tinyspring.ioc.annotation.inject;
import org.fhawk.tinyspring.util.CollectionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Created by tuo on 8/17/2016.
 */
public class IocHelper {
    private static Logger logger = LoggerFactory.getLogger(IocHelper.class);

    private IocHelper() {
    }

    static {
        try {
            for (Map.Entry<Class<?>, Object> entry : BeanHelper.getBeanMap().entrySet()) {
                Object instance = entry.getValue();
                Class<?> clazz = entry.getKey();
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(inject.class)) {
                        // 获取 Bean 字段对应的实现类
                        Class<?> implementClass = findImplementClass(field.getType());
                        // 若存在实现类，则执行以下代码
                        if (implementClass != null) {
                            // 从 Bean Map 中获取该实现类对应的实现类实例
                            Object implementInstance = BeanHelper.getBean(implementClass);
                            // 设置该 Bean 字段的值
                            if (implementInstance != null) {
                                field.setAccessible(true); // 将字段设置为 public
                                field.set(instance, implementInstance); // 设置字段初始值
                            } else {
                                logger.error("注入{}的属性{}失败，没有找到相应的Bean。", clazz.getName(), field.getType().getName());
                                throw new InitializationError("no bean of type " + field.getType().getName() + " field of " + clazz.getName());
                            }
                        } else {
                            setFiledValueWithoutImplementClass(field, field.getType(), instance);
                        }
                    }
                }
            }
        } catch (IllegalAccessException e) {
            logger.error("IOC 处理失败！", e);
        }
    }

    /**
     * 查找实现类
     */
    public static Class<?> findImplementClass(Class<?> interfaceClass) {
        Class<?> implementClass = interfaceClass;
        // 判断接口上是否标注了 Impl 注解
        if (interfaceClass.isAnnotationPresent(Impl.class)) {
            // 获取强制指定的实现类
            implementClass = interfaceClass.getAnnotation(Impl.class).value();
        } else {
            // 获取该接口所有的实现类
            List<Class<?>> implementClassList = ClassHelper.getBasePackageClassListBySuperClass(interfaceClass);
            if (CollectionUtil.isNotEmpty(implementClassList)) {
                // 获取第一个实现类
                implementClass = implementClassList.get(0);
            }
        }
        // 返回实现类对象
        return implementClass;
    }


    public static void refreshProxyClass(List<Class<?>> proxyClass) {
        try {
            //过滤需要注入属性的类
            List<Class<?>> classes = proxyClass.stream().filter(clazz -> Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(inject.class)).count() > 0).collect(Collectors.toList());
            //对需要注入的属性 进行注入
            for (Class<?> clazz : classes) {
                for (Field field : clazz.getDeclaredFields()) {
                    if (field.isAnnotationPresent(inject.class)) {
                        Field proxyField = BeanHelper.getBean(clazz).getClass().getDeclaredField(field.getName());
                        if (Objects.nonNull(proxyField)) {
                            field = proxyField;
                        }
                        // 获取 Bean 字段对应的实现类
                        Class<?> implementClass = findImplementClass(field.getType());
                        // 若存在实现类，则执行以下代码
                        if (implementClass != null) {
                            setFiledValueWithImplementClass(clazz, field, implementClass);
                        } else {
                            setFiledValueWithoutImplementClass(field, field.getType(), BeanHelper.getBean(clazz));
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error("处理代理类后，刷新IOC 失败。", e);
        }
    }

    private static void setFiledValueWithoutImplementClass(Field field, Class<?> type, Object bean) {
        Object implementInstance = BeanHelper.getBean(type);
        // 设置该 Bean 字段的值
        field.setAccessible(true); // 将字段设置为 public
        try {
            field.set(bean, implementInstance); // 设置字段初始值
        } catch (Exception e) {
            logger.error("设置类 {} 的属性 {} 失败", bean.getClass(), field.getType());
        }
    }

    private static void setFiledValueWithImplementClass(Class<?> clazz, Field field, Class<?> implementClass) {
        setFiledValueWithoutImplementClass(field, implementClass, BeanHelper.getBean(clazz));
    }
}
